package com.mmd.base.config;

import com.mmd.base.annotation.AnonymousAccess;
import com.mmd.base.hander.MyAccessDeniedHandler;
import com.mmd.base.hander.MyAuthenticationEntryPoint;
import com.mmd.base.hander.MyAuthFilter;
import com.mmd.base.hander.MyAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;
    @Autowired
    private MyAuthFilter myAuthFilter;
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 关比csrf
        http.csrf().disable();
        // 关比from登录表单
        http.formLogin().disable();
        Set<String> anonymousAccess = getAnonymousAccess();
        Set<String> nAnonymousAccess = getNoAnonymousAccess();
        http.exceptionHandling()
                .accessDeniedHandler(myAccessDeniedHandler)
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                // 不创建会话,因为该框架使用前后端分离模式开发 所以这里不使用session来保持会话
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // 允许通过的swagger文档资源路径
                .and()
                .authorizeRequests()
                .antMatchers("/swagger-ui.html").permitAll()
                .antMatchers("/swagger-resources/**").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/websocket/**").permitAll()
                .antMatchers("/*/api-docs").permitAll()
                .antMatchers("/v2/*").permitAll()
                .antMatchers("/doc.html").permitAll()
                .antMatchers("/error").permitAll()
                // 登录和退出等一系列不需要验证的操作不适用该ant来增加 使用注解{@link @AnonymousAccess}来标注
                // 匿名访问
                .antMatchers(anonymousAccess.toArray(new String[0])).permitAll()
                // 所有请求
                .anyRequest()
                // 经过认证的url
                .authenticated()
                .and()
                .addFilterBefore(myAuthFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(myAuthenticationProvider);
    }

    /**
     * 获取所有匿名访问类
     */
    private Set<String> getAnonymousAccess()
    {
        HashSet<String> urls = new HashSet<>();
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> requestMethod : handlerMethods.entrySet()) {
            HandlerMethod value = requestMethod.getValue();
            AnonymousAccess methodAnnotation = value.getMethodAnnotation(AnonymousAccess.class);
            if(methodAnnotation != null){
                Set<String> patterns = requestMethod.getKey().getPatternsCondition().getPatterns();
                urls.addAll(patterns);
            }
        }
        return urls;
    }

    /**
     * 获取所有非匿名访问类
     */
    private Set<String> getNoAnonymousAccess()
    {
        HashSet<String> urls = new HashSet<>();
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> requestMethod : handlerMethods.entrySet()) {
            HandlerMethod value = requestMethod.getValue();
            AnonymousAccess methodAnnotation = value.getMethodAnnotation(AnonymousAccess.class);
            if(methodAnnotation == null){
                Set<String> patterns = requestMethod.getKey().getPatternsCondition().getPatterns();
                urls.addAll(patterns);
            }
        }
        return urls;
    }
}
